//////Begin//////////////////////Modefied by Gujh for RaceBit///////////////////////////////////
//#include <elf.h>
//////End////////////////////////Modified by Gujh for RaceBit///////////////////////////////////
#include <string.h>
#include "crossbit/ELFMemoryImage.h"

using namespace crossbit;

ELFMemoryImage::ELFMemoryImage(	XTPStr pathname, 
								XTUint32 argc, 
								XTPStr argv[],
								XTPStr envp[] )
: MemoryImage(pathname, argc, argv, envp)
{
				mi_file = SYSCALL::XTOpen(pathname, O_RDONLY); 
				if( -1 == mi_file )
				{
								UTIL::XTLOG("open image error!\n");
								SYSCALL::XTExit(-1);
				}
}

ELFMemoryImage::~ELFMemoryImage()
{
				SYSCALL::XTClose(mi_file);
}

bool ELFMemoryImage::load()
{
				Elf32_Ehdr file_header;
				XTInt32	ret;
				XTInt32	sizeof_prog_headers;
				Elf32_Phdr *prog_headers;

				ret = SYSCALL::XTRead(mi_file, (void*)(&file_header), sizeof(Elf32_Ehdr));
				if( -1 == ret )
				{
								UTIL::XTLOG("Read ELFMemoryImage Header Error!");
								SYSCALL::XTExit(-1);
				}


//////Begin////////////////////////Added by Gujh for RaceBit///////////////////////////////////
				
				Elf32_Shdr	*elf_shdr = (Elf32_Shdr*)loadOneSection(file_header.e_shoff, file_header.e_shentsize * file_header.e_shnum);
				Elf32_Shdr	*tmp_sh_entry = elf_shdr + file_header.e_shstrndx;
				XTPStr	e_shstrn = loadOneSection(tmp_sh_entry->sh_offset,tmp_sh_entry->sh_size);

				//Load String Table
				Elf32_Half  string_table_index = 0;
				XTPStr  section_name = NULL;
				
				for (; string_table_index < file_header.e_shnum; string_table_index++)
				{
					string_table_index =  findSectionType(SHT_STRTAB, elf_shdr,file_header.e_shnum, string_table_index);
					tmp_sh_entry = elf_shdr + string_table_index;
					section_name = e_shstrn + (tmp_sh_entry->sh_name);
					if ((string_table_index != file_header.e_shstrndx) && !(strcmp(section_name,".strtab")))
						break;
				}
				
				if (string_table_index >= file_header.e_shnum)	
				{
								UTIL::XTLOG("Find Section Typied 'SHT_STRTAB' Error!");
								SYSCALL::XTExit(-1);
				} 
				string_table_size = tmp_sh_entry->sh_size;
				string_table_header = loadOneSection(tmp_sh_entry->sh_offset,string_table_size);
				
				UTIL::XTLOG("Load String Table OK!\n");
				printf("String Table size: %d\n", string_table_size);

				//Load Symbol Table
				Elf32_Half	symbol_table_index = findSectionType(SHT_SYMTAB, elf_shdr,file_header.e_shnum,0);
				tmp_sh_entry = elf_shdr + symbol_table_index;
				section_name = e_shstrn + (tmp_sh_entry->sh_name);
				
				if ((symbol_table_index >= file_header.e_shnum) || (strcmp(section_name,".symtab")))
				{
								UTIL::XTLOG("Find Section Typied 'SHT_SYMTAB' Error!");
								SYSCALL::XTExit(-1);
				}
				
				tmp_sh_entry = elf_shdr + symbol_table_index;
				symbol_table_elem_num = (tmp_sh_entry->sh_size)/(tmp_sh_entry->sh_entsize);
				symbol_table_header = (Elf32_Sym*)loadOneSection(tmp_sh_entry->sh_offset,tmp_sh_entry->sh_size);
				
				UTIL::XTLOG("Load Symbol Table OK!\n");
				printf("Symbol Table Element No.: %d\n", symbol_table_elem_num);

				delete[] e_shstrn;
				delete[] elf_shdr;


//////End//////////////////////////Added by Gujh for RaceBit///////////////////////////////////


				if( file_header.e_type != ET_EXEC )
				{
								UTIL::XTLOG("Not Executable Image!");
								SYSCALL::XTExit(-1);
				} 

				mi_entry = file_header.e_entry;
				sizeof_prog_headers = file_header.e_phentsize * file_header.e_phnum;
				prog_headers = new Elf32_Phdr[file_header.e_phnum]; 
				//need to replace with our function

				SYSCALL::XTLseek(mi_file, file_header.e_phoff, SEEK_SET);

				ret = SYSCALL::XTRead(mi_file, (void*)prog_headers, sizeof_prog_headers);
				if( -1 == ret )
				{
								UTIL::XTLOG("Read Prog Header Error!");
								SYSCALL::XTExit(-1);
				} 

				Elf32_Phdr *elf_phdr;
				XTInt32 elf_prot;
				XTInt32 elf_flags;
				XTInt32 page_offset;
				XTInt32 i;

				XTMemAddr highest_addr = 0x0;
				XTUint32  highest_file_size = 0;	
				XTUint32  highest_mem_size = 0;	

				for( i = 0, elf_phdr = (Elf32_Phdr*)(prog_headers); i < file_header.e_phnum; i++, elf_phdr++ )
				{
								if( elf_phdr->p_type != PT_LOAD )
												continue;

								if( elf_phdr->p_flags & PF_R )
												elf_prot |= PROT_READ;
								if( elf_phdr->p_flags & PF_W )
												elf_prot |= PROT_WRITE;
								if( elf_phdr->p_flags & PF_X )
												elf_prot |= PROT_EXEC;

								//elf_flags = MAP_ANON|MAP_PRIVATE|MAP_DENYWRITE|MAP_EXECUTABLE|MAP_FIXED;
								elf_flags = MAP_PRIVATE | MAP_FIXED;

								page_offset = PAGEOFFSET(elf_phdr->p_vaddr);

								if( MAP_FAILED == SYSCALL::XTMmap((void *)PAGESTART(elf_phdr->p_vaddr), elf_phdr->p_filesz + page_offset, elf_prot, elf_flags, mi_file, elf_phdr->p_offset - page_offset) )
								{
												UTIL::XTLOG("mmap error!\n");
												SYSCALL::XTExit(-1);
								}

#if 0
								XTUint8 *tmp_buf = new XTUint8[elf_phdr->p_filesz+1];
								//need TODO
								SYSCALL::XTLseek(mi_file, elf_phdr->p_offset, SEEK_SET);
								SYSCALL::XTRead(mi_file, tmp_buf, elf_phdr->p_filesz);
								UTIL::XTMemcpy((void *)elf_phdr->p_vaddr, tmp_buf, elf_phdr->p_filesz);
#endif

								if( elf_phdr->p_vaddr > highest_addr )
								{
												highest_addr = PAGESTART(elf_phdr->p_vaddr);
												highest_file_size = elf_phdr->p_filesz + page_offset;
												highest_mem_size = elf_phdr->p_memsz + page_offset;
								}
				}
				delete[] prog_headers;

				//Special deal with bss segment.
				elf_prot = PROT_READ | PROT_WRITE;
				elf_flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED;
				if( MAP_FAILED == SYSCALL::XTMmap((void *)PAGEALIGN(highest_addr + highest_file_size), (highest_mem_size - highest_file_size), elf_prot, elf_flags, 0, 0) )
				{
								UTIL::XTLOG("mmap error!\n");
								SYSCALL::XTExit(-1);
				}
				UTIL::XTMemset((void *)(highest_addr + highest_file_size), 0, (highest_mem_size-highest_file_size));

//				mi_brk = PAGEALIGN(highest_addr + highest_file_size) + PAGEALIGN(highest_mem_size - highest_file_size);
				mi_brk = PAGEALIGN(highest_addr + highest_mem_size);
				SYSCALL::sys_brk = mi_brk;
				//SYSCALL::sys_brk = 0x10017000;
				
				//map memory for heap & stack
				elf_prot = PROT_READ | PROT_WRITE;
				elf_flags = MAP_ANON | MAP_PRIVATE | MAP_FIXED;
				if( MAP_FAILED == SYSCALL::XTMmap((void *)PAGESTART(mi_brk), GLOBAL::STACK_BASE - mi_brk, elf_prot, elf_flags, 0, 0) )
				{
								UTIL::XTLOG("mmap error!\n");
								SYSCALL::XTExit(-1);
				}

				mi_stack = setupStack(GLOBAL::STACK_BASE);

				return true;
}


XTMemAddr ELFMemoryImage::map(XTMemAddr src)
{
#if 0
				//return (MemAddr)(mapOffset_+src);
				MemAddr ret;
				if( src == 0 )
								ret = s_map_offset;
				else
								ret = mapOffset_+src;
				return ret;
#endif
}


XTMemAddr ELFMemoryImage::setupStack( XTMemAddr base )
{
				XTUint32 i;
				XTMemAddr sp = base - MAX_ENVIRON;

				XTMemAddr p = sp;

				*(XTInt32*)p = mi_argc;
				p += sizeof(XTInt32);

				XTMemAddr argv_addr = p;
				p += (mi_argc+1) * sizeof(XTPStr);

				XTMemAddr envp_addr = p;
				for( i = 0; mi_envp[i]; ++ i )
								p += sizeof(XTPStr);
    		p += sizeof(XTPStr); // for NULL in the end of envp

				for( i = 0; i < mi_argc; i ++ )
				{
								UTIL::XTStrcpy((XTPStr)p, mi_argv[i]);
								((XTPStr*)argv_addr)[i] = (XTPStr)(p);
								p += UTIL::XTStrlen(mi_argv[i]) +1;
				}
				((XTPStr*)argv_addr)[i] = NULL;

				for( i = 0; mi_envp[i]; ++i)
				{
								UTIL::XTStrcpy((XTPStr)p, mi_envp[i]);
								((XTPStr*)envp_addr)[i] = (XTPStr)(p);
								p += UTIL::XTStrlen(mi_envp[i]) + 1;
				}
				((XTPStr*)envp_addr)[i] = NULL;

				return (XTMemAddr)(sp);
}

//////Begin////////////////////////Added by Gujh for RaceBit///////////////////////////////////


XTPStr  ELFMemoryImage::loadOneSection(XTInt32 file_offset, XTInt32 section_size)
{
	    XTInt32 ret;
	    XTPStr  section_content = new XTInt8[section_size];
	    SYSCALL::XTLseek(mi_file, file_offset, SEEK_SET);
	    ret = SYSCALL::XTRead(mi_file, (void*)section_content, section_size);
		if( -1 == ret )
           {
                   UTIL::XTLOG("Read ELFMemoryImage Section Error!");
                   SYSCALL::XTExit(-1);
           }
		return  section_content;
}

Elf32_Half ELFMemoryImage::findSectionType(Elf32_Word section_type, Elf32_Shdr *section_header, Elf32_Half section_num, Elf32_Half start_index) 
{ 
		Elf32_Half	indx = start_index;
		for (; indx < section_num; indx++)  
		{ 
			if (section_type == (section_header+indx)->sh_type)  
					return indx; 
		} 
		return section_num; 
}

XTMemAddr  ELFMemoryImage::lookupFuncSPC(XTPStr func_symbol_name)
{

	XTInt32	indx = 0;
	Elf32_Sym   *cur_symbol_entry = NULL;
	for(;indx < symbol_table_elem_num; indx++)
	{
		cur_symbol_entry = symbol_table_header + indx;

		if ((cur_symbol_entry->st_info &0xFU) != STT_FUNC)
			continue;
		
		if (!strcmp(func_symbol_name, (cur_symbol_entry->st_name)+string_table_header))
			break;
	}

	if (indx >= symbol_table_elem_num)
		return NULL;

	return (cur_symbol_entry->st_value);
}

XTPStr ELFMemoryImage::lookupFuncName(XTMemAddr func_SPC){
	XTInt32	indx = 0;
	Elf32_Sym   *cur_symbol_entry = NULL;
	for(;indx < symbol_table_elem_num; indx++)
	{
		cur_symbol_entry = symbol_table_header + indx;

		if ((cur_symbol_entry->st_info &0xFU) != STT_FUNC)
			continue;
		
		if (func_SPC == (cur_symbol_entry->st_value))
			break;
	}

	if (indx >= symbol_table_elem_num)
		return NULL;

	return ((cur_symbol_entry->st_name)+string_table_header);
}

//////End//////////////////////////Added by Gujh for RaceBit///////////////////////////////////
